package com.bigflexlib.x.flash.display.BitmapData
{
	
	
	import com.bigflexlib.utils.AsyncRunner;
	
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.Point;
	
	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	
	
	
	
	
	/**
	 * 
	 * Flood selector
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */	
	public class FloodSelector extends EventDispatcher
	{
		
		
		public var originalBD:BitmapData;
		public var bd:BitmapData;
		
		
		private var _clusters:ArrayCollection = new ArrayCollection( );
		[Bindable("clustersChanged")] // will never happen
		public function get clusters( ):IList
		{
			return _clusters;
		}
		
		
		
		
		private var runner:AsyncRunner;
		
		
		private var color2cluster:Array = [ ];
		
		
		
		private var async:Boolean = false;
		
		
		
	
		public function FloodSelector( bitmapData:BitmapData )
		{
			
			bd = new BitmapData( bitmapData.width, bitmapData.height, true, 0xff000000 );	

			var matrix:Array = [
				2, -1, -1, 0, 0,
				0, 0, 0, 0, 0,
				0 ,0 ,0 ,0, 0,
				0 ,0 ,0 ,2 ,0
			];

            bd.applyFilter( bitmapData, bitmapData.rect, new Point(), new ColorMatrixFilter( matrix ) ); 			
			
			bd.threshold( bd, bd.rect, new Point( ), ">", 100, 0xffff0000, 0x00ff0000 );

		}
		
		
		
		
		
		public function getClusterAtPoint( point:Point ):FloodCluster
		{
			
			var pointColor:uint = bd.getPixel32( point.x, point.y );
			
			if ( pointColor == 0xff000000 ) 	// no cluster here ( black )
			{
				return null;
			}
			if ( pointColor == 0xffff0000 ) 	// in cluster ( red ), but not processed yet
			{
				var newFillColor:uint = getNonOccurringColor( );
					
				bd.floodFill( point.x, point.y, newFillColor );

				var cluster:FloodCluster = new FloodCluster( this, newFillColor );
				
				color2cluster[ uint(newFillColor) ] = cluster;
				
				_clusters.addItem( cluster );
				
				return cluster;
			}
			
			// TODO: if cluster has a small size... should we return null?
			return color2cluster[ uint(pointColor) ];

		}
		
		
		
		
		
		
		
		public function processAllClusters( ):void
		{
			if ( async )
			{
				runner = AsyncRunner.create( $processAllClusters );
				runner.addEventListener( Event.COMPLETE, runner_complete );	
				runner.runEachFrame( 100 );
			}
			else
			{
				$processAllClusters( );
				dispatchEvent( new Event( Event.COMPLETE ) );	
			}
			
		}
		
		
		private function runner_complete( event:Event ):void
		{
			dispatchEvent( new Event( Event.COMPLETE ) );
		}
		
		
		
		
		
		private function $processAllClusters( ):void
		{
			var x:int, y:int;
			for ( x=0; x<bd.width; x+=5 )
				for ( y=0; y<bd.height; y+=5 )
				{
					if ( async )
						AsyncRunner.queue( getClusterAtPoint, [new Point( x, y )] );
					else
						getClusterAtPoint( new Point( x, y ) );
				}
		}
		
		
		
		
		
		
		
		
		// important. not all colors are valid for floodFill
		private var serial:uint=0xffff0001;
		
		private function getNonOccurringColor( ):uint
		{
			if ( (serial++) == 0xffff0000 )// red has a special meaning
				serial++;
			return serial;
		}
		
		
		
		


		

	}
	
}